﻿/*
    创建日期: 2013.6.4
    创建者:张存
    邮箱:zhangcunliang@126.com
    修改记录:
        2016.5.9    反射设置属性值时加上DBNull的判断.
        2016.8.20   反射设置时属性判断 CanWrite 
        2018.5.24   增加获取方法特性的方法，可获取调用该方法特性的对象
        2020.1.13   反射设置属性 改为 object类型
        2020.2.19   增加 SetPropertyDefaultValue 方法
        2020.2.23   根据对象及属性名（或DataRow的列名）获取值
        2020.5.14   增加创建实例方法 CreateInstance(fullType);
        2020.5.17   SetPropertyValue 方法增加 DataRow 赋值的实现
        2020.11.26  增加对象反射复制可忽略指定属性的实现
 */
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Data;
using System.Diagnostics;
using System.Linq;

namespace ZhCun.Utils.Helpers
{
    /// <summary>
    /// 反射操作的封装
    /// </summary>
    public class ReflectionHelper
    {
        /// <summary>
        /// 获得对象的所有公共属性信息
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <returns>返回属性信息</returns>
        public static PropertyInfo[] GetPropertyInfo<T>() where T : class
        {
            Type t = typeof(T);
            PropertyInfo[] proInfo = t.GetProperties();
            return proInfo;
        }
        /// <summary>
        /// 获得指定对象的所有公共属性信息
        /// </summary>
        public static PropertyInfo[] GetPropertyInfo(object obj)
        {
            Type t = obj.GetType();
            PropertyInfo[] proInfo = t.GetProperties();
            return proInfo;
        }
        /// <summary>
        /// 根据类型获取属性元数据
        /// </summary>
        /// <param name="objType">对象类型</param>
        /// <param name="pName">属性名</param>
        /// <returns>属性元数据</returns>
        public static PropertyInfo GetPropertyInfo(Type objType, string pName)
        {
            PropertyInfo proInfo = objType.GetProperty(pName, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance);

            return proInfo;
        }
        /// <summary>
        /// 根据属性名获取属性信息
        /// </summary>
        /// <param name="obj">目标对象</param>
        /// <param name="pName">属性名称</param>
        /// <returns>返回属性类型对象</returns>
        public static PropertyInfo GetPropertyInfo(object obj, string pName)
        {
            if (obj == null || pName.IsEmpty()) return null;
            Type objType = obj.GetType();
            return GetPropertyInfo(objType, pName);
        }

        /// <summary>
        /// 根据属性名获取属性信息
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="pName">属性名称</param>
        /// <returns>返回属性类型对象</returns>
        public static PropertyInfo GetPropertyInfo<T>(string pName) where T : class
        {
            Type objType = typeof(T);
            return GetPropertyInfo(objType, pName);
        }
        /// <summary>
        /// 获得对象的所有公共属性的属性名
        /// </summary>        
        public static string[] GetPropertyNames<T>() where T : class
        {
            PropertyInfo[] pInfos = GetPropertyInfo<T>();
            if (pInfos != null)
            {
                List<string> pNamesList = new List<string>();
                foreach (PropertyInfo item in pInfos)
                {
                    pNamesList.Add(item.Name);
                }
                return pNamesList.ToArray();
            }
            return null;
        }
        /// <summary>
        /// 根据对象类型获取所有公共属性名
        /// </summary>
        public static string[] GetPropertyNames(Type objType)
        {
            PropertyInfo[] pInfos = objType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
            if (pInfos != null)
            {
                List<string> pNamesList = new List<string>();
                foreach (PropertyInfo item in pInfos)
                {
                    pNamesList.Add(item.Name);
                }
                return pNamesList.ToArray();
            }
            return null;
        }
        /// <summary>
        /// 获得对象的属性名和属性值
        /// </summary>
        public static Dictionary<string, object> GetPropertyNameAndValue(object obj)
        {
            PropertyInfo[] pInfos = GetPropertyInfo(obj);
            if (pInfos != null)
            {
                Dictionary<string, object> pNameAndValue = new Dictionary<string, object>();
                foreach (PropertyInfo item in pInfos)
                {
                    string pName = item.Name;
                    object pValue = item.GetValue(obj, null);
                    pNameAndValue.Add(item.Name, pValue);
                }
                return pNameAndValue;
            }
            return null;
        }
        /// <summary>
        /// 获取指定对象或者DataRow的属性值
        /// </summary>
        /// <param name="obj">如果obj为DataRow则返回指定列的值，否则为属性值</param>
        /// <param name="pName">属性名或者DataRow的列名</param>
        public static object GetPropertyValue(object obj, string pName)
        {
            if (obj is DataRow dr)
            {
                if (!dr.Table.Columns.Contains(pName)) return null;
                return dr[pName];
            }
            else
            {
                var pInfo = GetPropertyInfo(obj, pName);
                if (pInfo == null) return null;
                return pInfo.GetValue(obj, null);
            }
        }
        /// <summary>
        /// 获取指定对象或者DataRow的属性值，返回强类型
        /// </summary>
        /// <typeparam name="T">返回的数据类型</typeparam>
        /// <param name="obj">数据对象（DataRow 或者 对象）</param>
        /// <param name="pName">属性名或者DataRow列名</param>
        /// <returns>返回T</returns>
        public static T GetPropertyValue<T>(object obj, string pName)
        {
            var r = GetPropertyValue(obj, pName);
            if (r == null) return default;
            var targetType = typeof(T);
            if (r.GetType() != targetType)
            {
                //如果这个方法报错，调试一下看如何错误
                r = Convert.ChangeType(r, targetType);
            }
            return (T)r;
        }

        /// <summary>
        /// 赋值一个对象的属性值到另一个对象对象（深拷贝）
        /// </summary>
        public static void CopyObject(object targetObj, object sourceObj, params string[] ignorePropertys)
        {
            Type tarType = targetObj.GetType();
            PropertyInfo[] tarProInfos = tarType.GetProperties();

            foreach (var tarPro in tarProInfos)
            {
                if (ignorePropertys.Contains(tarPro.Name)) continue;
                //目标属性信息
                PropertyInfo srcProInfo = GetPropertyInfo(sourceObj, tarPro.Name);
                if (srcProInfo == null) continue;

                if (tarPro.CanWrite)
                {
                    //源属性值
                    object sourcePropertyValue = srcProInfo.GetValue(sourceObj, null);
                    if (tarPro.PropertyType != srcProInfo.PropertyType)
                    {
                        //不相同的类型跳过,或者强制转换
                        continue;
                    }
                    tarPro.SetValue(targetObj, sourcePropertyValue, null);
                }
            }
        }

        /// <summary>
        /// 设置一个对象的属性值
        /// 根据一个源对象赋值属性内容到一个目标对象中,
        /// 即:如果sourceObj对象和targetObj对象属性相同则赋值,可用于视图转换表实体的应用
        /// </summary>
        /// <param name="targetObj">要设置的目标对象</param>
        /// <param name="sourceObj">要设置的源对象</param>
        public static void SetPropertyValue(object targetObj, object sourceObj)
        {
            CopyObject(targetObj, sourceObj);

            //Type sourceType = sourceObj.GetType();
            //PropertyInfo[] sourcePInfos = sourceType.GetProperties();
            //foreach (var item in sourcePInfos)
            //{
                
            //    //目标属性信息
            //    PropertyInfo pInfo = GetPropertyInfo(targetObj, item.Name);
            //    if (pInfo != null && pInfo.CanWrite)
            //    {
            //        //源属性值
            //        object sourcePropertyValue = item.GetValue(sourceObj, null);
            //        pInfo.SetValue(targetObj, sourcePropertyValue, null);
            //    }
            //}
        }

        //设置 属性值 改为上边的 object 对象
        ///// <summary>
        ///// 根据一个源对象赋值属性内容到一个目标对象中,
        ///// 即:如果sourceObj对象和targetObj对象属性相同则赋值,可用于视图转换表实体的应用
        ///// </summary>
        //public static void SetPropertyValue<T>(T targetObj, object sourceObj) where T : class
        //{
        //    SetObjectPropertyValue(targetObj, sourceObj);
        //}

        //public static void SetPropertyValue<T>(T obj, string pName, object pValue) where T : class, new()
        //{
        //    SetObjectPropertyValue(obj, pName, pValue);
        //}

        /// <summary>
        /// 将指定对象的所有公共属性改未默认值
        /// </summary>
        public static void SetPropertyDefaultValue(object obj)
        {
            Type t = obj.GetType();
            PropertyInfo[] proInfo = t.GetProperties();
            foreach (var item in proInfo)
            {
                item.SetValue(obj, default, null);
            }
        }
        /// <summary>
        /// 将对象的指定属性改未默认值
        /// </summary>
        public static void SetPropertyDefaultValue(object obj, string pName)
        {
            PropertyInfo pInfo = GetPropertyInfo(obj, pName);
            if (pInfo != null && pInfo.CanWrite)
            {
                pInfo.SetValue(obj, default, null);
            }
        }

        /// <summary>
        /// 根据属性名设置对象的 属性值
        /// </summary>
        public static void SetPropertyValue(object target, string pName, object pValue)
        {
            if (string.IsNullOrEmpty(pName)) return;
            if (pValue == null || DBNull.Value.Equals(pValue) || Guid.Empty.Equals(pValue)) return;

            if (target is DataRow dr)
            {
                dr[pName] = pValue;
                return;
            }

            PropertyInfo pInfo;
            if (target is Type type)
            {
                //静态类
                pInfo = type.GetProperty(pName);
            }
            else
            {
                pInfo = GetPropertyInfo(target, pName);
            }
            if (pInfo != null && pInfo.CanWrite)
            {
                if (pInfo.PropertyType != pValue.GetType())
                {
                    var targetType = Nullable.GetUnderlyingType(pInfo.PropertyType);
                    if (targetType == null)
                    {
                        targetType = pInfo.PropertyType;
                    }
                    if (targetType == typeof(Guid))
                    {
                        pValue = new Guid(pValue.ToString());
                    }
                    else
                    {
                        //TODO: 如果不能直接转换根据类型单独调试更改
                        pValue = Convert.ChangeType(pValue, targetType);
                    }
                }
                pInfo.SetValue(target, pValue, null);
            }
        }

        /// <summary>
        /// 根据属性名设置对象的 属性值
        /// </summary>
        public static void SetPropertyValue(object target, Dictionary<string, object> pNameValue)
        {
            foreach (string item in pNameValue.Keys)
            {
                SetPropertyValue(target, item, pNameValue[item]);
            }
        }
        /// <summary>
        /// 根据一个DataRow对象设置对象的属性值
        /// </summary>
        /// <param name="obj">要赋值的对象,不能为空</param>
        /// <param name="dr">数据行</param>
        /// <returns>返回T</returns>
        public static void SetPropertyValue(object obj, DataRow dr)//where T : class, new()
        {
            //if (dr == null) return;
            //if (dr.Table.Columns.Count == 0) return;
            Dictionary<string, object> pNameAndValue = new Dictionary<string, object>();
            foreach (DataColumn item in dr.Table.Columns)
            {
                if (dr[item.ColumnName] != DBNull.Value)
                    pNameAndValue.Add(item.ColumnName, dr[item.ColumnName]);
            }
            SetPropertyValue(obj, pNameAndValue);
        }

        /// <summary>
        /// 根据一个DataRow对象设置对象的属性值
        /// </summary>
        /// <param name="dr">数据行</param>
        /// <returns>返回T</returns>
        public static T SetPropertyValue<T>(DataRow dr)
            where T : class, new()
        {
            T t = new T();
            SetPropertyValue(t, dr);
            return t;
        }
        /// <summary>
        /// 根据类型设置对象的默认属性值
        /// </summary>
        public static void SetPropertyValue<T>(T obj, Dictionary<Type, object> pTypeAndValue)
            where T : class, new()
        {
            PropertyInfo[] pInfos = GetPropertyInfo<T>();
            if (pInfos == null) return;
            foreach (PropertyInfo item in pInfos)
            {
                if (!item.CanWrite) continue;
                Type itemType = item.PropertyType;
                if (pTypeAndValue.ContainsKey(itemType))
                {
                    //object newValue = Convert.ChangeType(pValue, pInfo.PropertyType);
                    item.SetValue(obj, pTypeAndValue[itemType], null);
                }
            }
        }
        /// <summary>
        /// 根据类型设置对象的空值的默认值
        /// </summary>
        public static void SetPropertyNullValue<T>(T obj, Dictionary<Type, object> pTypeAndValue) where T : class, new()
        {
            PropertyInfo[] pInfos = GetPropertyInfo<T>();
            if (pInfos == null) return;
            foreach (PropertyInfo item in pInfos)
            {
                if (!item.CanWrite) continue;
                Type itemType = item.PropertyType;
                object attrValue = item.GetValue(obj, null);

                if (pTypeAndValue.ContainsKey(itemType))
                {
                    if (attrValue == null)
                    { }
                    else if (itemType == typeof(DateTime) && Convert.ToDateTime(attrValue) == new DateTime())
                    {
                        item.SetValue(obj, pTypeAndValue[itemType], null);
                    }
                    else if (itemType == typeof(Guid) && new Guid(attrValue.ToString()) == Guid.Empty)
                    {
                        item.SetValue(obj, pTypeAndValue[itemType], null);
                    }
                }

            }
        }

        /// <summary>
        /// 反射创建一个对象
        /// </summary>
        /// <typeparam name="T">要创建对象的类型</typeparam>
        /// <param name="assemblyName">项目名称(程序集名称,dll文件名)</param>
        /// <param name="typeName">类型名称,类的类型</param>
        /// <returns>返回一个T对象</returns>
        public static T CreateInstance<T>(string assemblyName, string typeName) where T : class
        {
            var obj = CreateInstance(assemblyName, typeName);
            return obj as T;
        }
        /// <summary>
        /// 反射创建一个对象
        /// </summary>
        /// <param name="assemblyName">项目名称(程序集名称,dll文件名)</param>
        /// <param name="typeName">类型名称,类的类型</param>
        /// <returns>返回结果对象</returns>
        public static object CreateInstance(string assemblyName, string typeName)
        {
            Assembly assembly = Assembly.Load(assemblyName);
            Type type = assembly.GetType(typeName, true, true);
            object obj = Activator.CreateInstance(type);
            return obj;
        }
        /// <summary>
        /// 反射创建一个对象，根据标准格式：类名称,程序集
        /// </summary>
        /// <param name="fullType">name格式为：“类全名,程序集”</param>
        /// <returns>实例对象</returns>
        public static object CreateInstance(string fullType)
        {
            var typeStrs = fullType.Split(',');
            string typeName = typeStrs[0];
            string asmName = typeStrs[1];
            var obj = CreateInstance(asmName, typeName);
            return obj;
        }
        /// <summary>
        /// 反射创建一个对象，根据标准格式：类名称,程序集
        /// </summary>
        /// <param name="fullType">name格式为：“类全名,程序集”</param>
        /// <returns>实例对象</returns>
        public static T CreateInstance<T>(string fullType) where T : class
        {
            var obj = CreateInstance(fullType);
            return obj as T;
        }

        /// <summary>
        /// 获得指定成员的特性对象
        /// </summary>
        /// <typeparam name="T">要获取属性的类型</typeparam>
        /// <param name="pInfo">属性原型</param>
        /// <returns>返回T对象</returns>
        public static T GetCustomAttribute<T>(PropertyInfo pInfo) where T : Attribute, new()
        {
            Type attributeType = typeof(T);
            Attribute attrObj = Attribute.GetCustomAttribute(pInfo, attributeType);
            T rAttrObj = attrObj as T;
            return rAttrObj;
        }

        /// <summary>
        /// 获取指定方法的原型的特性对象
        /// </summary>
        public static TMethodAttribute GetMethodAttribute<TMethodAttribute>(MethodBase method, bool inherit = true)
            where TMethodAttribute : Attribute, new()
        {
            var attrs = method.GetCustomAttributes(inherit);
            foreach (var item in attrs)
            {
                bool r = item.GetType() == typeof(TMethodAttribute);
                if (r)
                {
                    return item as TMethodAttribute;
                }
            }
            return null;
        }

        /// <summary>
        /// 根据堆栈信息获取当前请求该方法是否包含指定方法特性，如果包含则返回特性对象，否则返回false
        /// </summary>
        /// <typeparam name="TMethodAttribute">方法所标注的特性类型</typeparam>
        /// <returns>返回方法特性对象</returns>
        public static TMethodAttribute GetMethodAttribute<TMethodAttribute>(bool inherit = true) where TMethodAttribute : Attribute, new()
        {
            StackTrace trace = new StackTrace();
            var frames = trace.GetFrames();
            foreach (var frame in frames)
            {
                var method = frame.GetMethod();
                var r = GetMethodAttribute<TMethodAttribute>(method, inherit);
                if (r != null)
                {
                    return r;
                }
            }
            return null;
        }
        /// <summary>
        /// 根据Type获取类的自定义特性对象
        /// </summary>
        public static TAttr GetClassAttribute<TAttr>(Type t) where TAttr : Attribute
        {
            object[] attrs = t.GetCustomAttributes(typeof(TAttr), true);
            if (attrs != null)
            {
                foreach (object item in attrs)
                {
                    if (item is TAttr)
                    {
                        return item as TAttr;
                    }
                }
            }
            return null;
        }
        /// <summary>
        /// 获得自定义特性对象
        /// </summary>
        /// <typeparam name="TAttr">特性类型</typeparam>
        /// <typeparam name="TClass">实体类型</typeparam>
        /// <returns></returns>
        public static TAttr GetClassAttribute<TAttr, TClass>() where TAttr : Attribute
        {
            Type t = typeof(TClass);
            return GetClassAttribute<TAttr>(t);
        }
        /// <summary>
        /// 根据对象获取自定义特性对象
        /// </summary>
        public static TAttr GetClassAttribute<TAttr>(object obj) where TAttr : Attribute
        {
            if (obj == null) return null;
            Type t = obj.GetType();
            return GetClassAttribute<TAttr>(t);
        }


    }
}